package ge.unihack.instantdj.protocol.client;

import ge.unihack.instantdj.protocol.common.Command;
import ge.unihack.instantdj.protocol.common.CommandReader;
import ge.unihack.instantdj.protocol.common.JSONCommandReader;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

public class InstantDJClient implements Client {

	private static final String TAG = "InstantDJClient";
	
	private Socket sock;
	private boolean connected;
	private CommandReader commandReader = new JSONCommandReader();
	private CommandListener commandListener;
	private Handler handler;
	
	public void setCommandReader(CommandReader commandReader) {
		this.commandReader = commandReader;
	}

	@Override
	public void setCommandListener(CommandListener cl) {
		setCommandListener(cl, null);
	}

	@Override
	public void setCommandListener(CommandListener cl, Looper looper) {
		commandListener = cl;
		if (looper != null) {
			handler = new Handler(looper);
		}
	}

	@Override
	public void connect(String host, int port) throws IOException {
		if (sock == null) {
			sock = new Socket(host, port);
			connected = true;
			listenCommands();
		}
	}

	private void listenCommands() {
		new Thread() {
			public void run() {
				while (connected) {
					try {
						InputStream in = sock.getInputStream();
						Command command = commandReader.readCommand(in);
						if (command != null) {
							fireOnCommand(command);
						} else {
							disconnect();
						}
					} catch (IOException e) {
						Log.w(TAG, "Connection to " + sock + " closed unexpectedly");
						if (connected) {
							disconnect();
						}
					}
				}
			};
		}.start();
	}

	private void fireOnCommand(final Command command) {
		if (handler != null) {
			handler.post(new Runnable() {
				
				@Override
				public void run() {
					commandListener.onCommand(command);
				}
			});
		} else {
			commandListener.onCommand(command);
		}
	}
	
	@Override
	public void disconnect() {
		if (sock != null) {
			connected = false;
			try {
				sock.close();
			} catch (IOException e) {
			}
			sock = null;
			Log.i(TAG, "Disconnected");
		}
	}

}
